/**
 *  @file: ConsoleTask.cpp
 *  @author: nwz
 *  @brief: 
 *  @date: 2024��7��25��
 * 
 */

#include <code/task/ConsoleTask.h>
#include "all_header.h"

#define ABORT() ASSERT(0)

extern "C" {
// send callback
extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef handle_GPDMA1_Channel1;
static SemaphoreHandle_t send_dma_sem;

// read param
enum class DmaQueType : uint8_t {
	TIMEOUT, COMPLETE, HALF_COMPLETE
};
typedef struct {
	DmaQueType type;
	uint8_t *data;
	uint32_t len;
	TickType_t ticks;
} dma_queue_data_t;
static QueueHandle_t read_dma_que;
uint8_t read_dma_buffer[CONSOLE_DMA_RECV_BUFFER_SIZE];

void console_uart_send_complete_callback(UART_HandleTypeDef *huart) { // add in dma irq
	if (xSemaphoreGive(send_dma_sem) != pdTRUE) {
		ABORT();
	}
}

static void process_read_dma(DmaQueType type) {
	static int start_pos = 0;

	dma_queue_data_t dma_data;
	MEM_ZERO(dma_data);
	uint8_t *buffer = read_dma_buffer;
	uint32_t buffer_size = sizeof(read_dma_buffer);

	dma_data.type = type;
	dma_data.ticks = xTaskGetTickCountFromISR();
	int size = handle_GPDMA1_Channel1.Instance->CDAR - (uint32_t) buffer;

	dma_data.data = buffer + start_pos;
	if (start_pos == size) { // must be timeout event
		dma_data.len = 0;
	} else {
		dma_data.len = (size == 0 ? buffer_size : size) - start_pos;
	}

	BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
	if (xQueueSendFromISR(read_dma_que, &dma_data,
			&pxHigherPriorityTaskWoken) != pdTRUE) {
		ABORT();
	}

	start_pos = size;
}

// recv callback
void console_uart_recv_complete_callback(UART_HandleTypeDef *huart) {
	process_read_dma(DmaQueType::COMPLETE);
}

void console_uart_recv_halfcomplete_callback(UART_HandleTypeDef *huart) {
	process_read_dma(DmaQueType::HALF_COMPLETE);
}

void console_uart_recv_timeout_callback(UART_HandleTypeDef *huart) {
	process_read_dma(DmaQueType::TIMEOUT);
}

}

namespace task {

ConsoleTask::ConsoleTask() {
	// TODO Auto-generated constructor stub

}

ConsoleTask::~ConsoleTask() {
	// TODO Auto-generated destructor stub
}

ConsoleTask& ConsoleTask::getInstance() {
	static ConsoleTask console;
	return console;
}

void ConsoleTask::writeLoop(ConsoleTask *console) {
	static uint8_t buffer[CONSOLE_LOG_BUFFER_SIZE / 2];
	send_dma_sem = xSemaphoreCreateBinary();
	if (!send_dma_sem) {
		ABORT();
	}

	while (true) {
		size_t size = xStreamBufferReceive(console->send_stream_buffer, buffer,
				sizeof(buffer), portMAX_DELAY);
		if (size > 0) {
			///< uart dma send
			if (HAL_UART_Transmit_DMA(&huart1, buffer, size) != HAL_OK) {
				ABORT();
			}
			if (xSemaphoreTake(send_dma_sem, portMAX_DELAY) != pdTRUE) {
				ABORT();
			}
		}
	}
}

void ConsoleTask::readLoop(ConsoleTask *console) {
	static uint8_t data[CONSOLE_MAX_FRAME_SIZE];
	const uint32_t data_size = sizeof(data);
	dma_queue_data_t dma_data;

	read_dma_que = xQueueCreate(2, sizeof(dma_queue_data_t));
	if (!read_dma_que) {
		ABORT();
	}

	if (HAL_UART_Receive_DMA(&huart1, read_dma_buffer, sizeof(read_dma_buffer))
			!= HAL_OK) {
		ABORT();
	}

	int start_pos = 0; ///< 残留下来的数据
	while (true) {
		if (xQueueReceive(read_dma_que, &dma_data,
		portMAX_DELAY) != pdTRUE) {
			ABORT();
		}

		// too much data
		int t_size = 0;
		while (dma_data.len + start_pos >= data_size) {
			int copy_len = std::min((int) (data_size - start_pos),
					(int) dma_data.len);
			dma_data.len -= copy_len;
			memcpy(data + start_pos, dma_data.data + t_size, copy_len);
			console->sendDataToUserRead(data, data_size);
			t_size += copy_len;
			start_pos = 0;
		}

		// copy rest data
		if (dma_data.len > 0) {
			memcpy(data + start_pos, dma_data.data + t_size, dma_data.len);
			start_pos += dma_data.len;
		}
		// split
		if (start_pos > 0) {
			int send_pos = 0;
			for (int i = 0; i < start_pos; i++) {
				if (data[i] == '\r') {
					if (i < start_pos - 1 && data[i + 1] == '\n') {
						i += 1;
					}
					console->sendDataToUserRead(data + send_pos,
							i + 1 - send_pos);
					send_pos = i + 1;
				}
			}
			if (send_pos > 0) {
				start_pos -= send_pos;
				if (start_pos > 0) {
					for (int i = 0; i < start_pos; i++) {
						data[i] = data[send_pos + i];
					}
				}
			}
		}

		// timeout event
		if (dma_data.type == DmaQueType::TIMEOUT) {
			if (start_pos > 0) {
				console->sendDataToUserRead(data, start_pos);
				start_pos = 0;
			}
		}
	}
}

void ConsoleTask::sendDataToUserRead(const uint8_t *data, uint32_t len) {
	auto size = xMessageBufferSpacesAvailable(read_message_buffer);
	if (len > size) {
		ABORT();
	}
	if (xMessageBufferSend(read_message_buffer, data, len,
			DEFAULT_WAIT_TICKS) != len) {
		ABORT();
	}
}

void ConsoleTask::logSync(const void *fmt, ...) {
	static uint8_t buffer[CONSOLE_LOG_BUFFER_SIZE];
	va_list list;
	va_start(list, fmt);
	int m = vsnprintf((char*) buffer, sizeof(buffer), (const char*) fmt, list);
	va_end(list);

	if (m > 0)
		ASSERT(HAL_UART_Transmit(&huart1, buffer, m, 1000) == HAL_OK);
}

void ConsoleTask::log(LogLevel level, const uint8_t *tag, const void *fmt,
		...) {
	static uint8_t buffer[CONSOLE_LOG_BUFFER_SIZE];
	if (level < this->lv) {
		return;
	}

	if (xStreamBufferIsFull(send_stream_buffer)) {
		ABORT();
	}

	if (xSemaphoreTake(send_mutex, portMAX_DELAY) != pdTRUE) {
		ABORT();
	}

#define TM_FORMAT "%-10d"
	auto tm = xTaskGetTickCount();
	switch (level) {
	case LogLevel::DEBUG:
		snprintf((char*) buffer, sizeof(buffer), TM_FORMAT "[DEBUG][%s] ", tm,
				tag);
		break;
	case LogLevel::INFO:
		snprintf((char*) buffer, sizeof(buffer), TM_FORMAT "[INFO][%s] ", tm,
				tag);
		break;
	case LogLevel::WARN:
		snprintf((char*) buffer, sizeof(buffer), TM_FORMAT "[WARN][%s] ", tm,
				tag);
		break;
	case LogLevel::ERR:
		snprintf((char*) buffer, sizeof(buffer), TM_FORMAT "[ERROR][%s] ", tm,
				tag);
		break;
	}

	xStreamBufferSend(send_stream_buffer, buffer, strlen((const char*) buffer),
			0);

	va_list valist;
	va_start(valist, fmt);
	int m = vsnprintf((char*) buffer, sizeof(buffer), (const char*) fmt,
			valist);
	va_end(valist);

	if (m > 0) {
		xStreamBufferSend(send_stream_buffer, buffer, m, 0);
	}
	if (xSemaphoreGive(send_mutex) != pdTRUE) {
		ABORT();
	}
}

int ConsoleTask::write(const uint8_t *data, uint32_t size) {
	int ret = 0;
	if (xStreamBufferIsFull(send_stream_buffer)) {
		ABORT();
	}
	if (xSemaphoreTake(send_mutex, portMAX_DELAY) != pdTRUE) {
		ABORT();
	}

	ret = xStreamBufferSend(send_stream_buffer, data, size, 0);

	if (xSemaphoreGive(send_mutex) != pdTRUE) {
		ABORT();
	}

	return ret;
}

int ConsoleTask::read(uint8_t *data, uint32_t len, uint32_t timeout) {
	int ret = 0;
	ret = xMessageBufferReceive(read_message_buffer, data, len, timeout);
	return ret;
}

int ConsoleTask::init() {
	int ret = -1;
	do {
		send_mutex = xSemaphoreCreateMutex();
		if (!send_mutex) {
			ABORT();
		}

		send_stream_buffer = xStreamBufferCreate(CONSOLE_SEND_BUFFER_SIZE, 1);
		if (!send_stream_buffer) {
			ABORT();
		}

		read_message_buffer = xMessageBufferCreate(CONSOLE_RECV_BUFFER_SIZE);
		if (!read_message_buffer) {
			ABORT();
		}

		HAL_UART_ReceiverTimeout_Config(&huart1, huart1.Init.BaudRate * CONSOLE_RECV_TIMEOUT /1000);

		if (HAL_UART_EnableReceiverTimeout(&huart1) != HAL_OK) {
			ABORT();
		}

		ret = 0;
	} while (0);
	return 0;
}

void ConsoleTask::test1() {
	uint8_t buffer[CONSOLE_MAX_FRAME_SIZE] = { 0 };
//	auto console = getInstance();
//
//	while (true) {
//		int len = console->read(buffer, sizeof(buffer));
//		if (len > 0) {
//			console->write(buffer, len);
//		}
//	}
}

} /* namespace task */
